﻿using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.Command;
using Gat.Controls;
using NativeWifi;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Windows.Threading;
using Telerik.Windows.Controls;

namespace WiFiAccessPointScanner
{
    public class MainViewModel : GalaSoft.MvvmLight.ViewModelBase
    {
        private bool isCollecting;
        private bool isContinuous;
        private bool isXmlFormatSelected;
        private bool isJsonFormatSelected;
        private bool isNumberSuffixSelected;
        private int numberOfSamples;
        private int sampleCount;
        private int scanInterval;
        private int sessionCount;
        private double progressIncrement;
        private double progressVal;
        private double currentXPos;
        private double currentYPos;
        private string busyIndicatorString;
        private string outputPath;
        private string fileNamePrefix;

        private RelayCommand browseFolderCommand;
        private RelayCommand startCommand;
        private RelayCommand stopCommand;
        private RelayCommand exportCommand;

        private WlanAdapter wLanAdapter;
        private List<Session> sessions;
        private ObservableCollection<TraceEntry> traceEntries;
        private Thread traceCollectingThread;

        public MainViewModel()
        {
            wLanAdapter = new WlanAdapter();
            scanInterval = 5;
            numberOfSamples = 10;

            sessions = new List<Session>();
            traceEntries = new ObservableCollection<TraceEntry>();
            browseFolderCommand = new RelayCommand(BrowseFolder);
            startCommand = new RelayCommand(StartCollecting, CanStart);
            stopCommand = new RelayCommand(StopCollecting, CanStop);
            exportCommand = new RelayCommand(Export, CanExport);

            traceCollectingThread = new Thread(CollectTraces) { IsBackground = true };
            traceCollectingThread.Start();
        }

        public string FileNamePrefix
        {
            get { return fileNamePrefix; }
            set
            {
                fileNamePrefix = value;
                RaisePropertyChanged(() => FileNamePrefix);
                ExportCommand.RaiseCanExecuteChanged();
            }
        }

        public string OutputFolder
        {
            get { return outputPath; }
            set
            {
                outputPath = value;
                RaisePropertyChanged(() => OutputFolder);
                ExportCommand.RaiseCanExecuteChanged();
            }
        }

        public string BusyIndicatorText
        {
            get { return busyIndicatorString; }
            set
            {
                busyIndicatorString = value;
                RaisePropertyChanged(() => BusyIndicatorText);
            }
        }

        public int ScanInterval
        {
            get { return scanInterval; }
            set
            {
                scanInterval = value;
                RaisePropertyChanged(() => ScanInterval);
            }
        }

        public int SampleCount
        {
            get { return sampleCount; }
            set
            {
                sampleCount = value;
                RaisePropertyChanged(() => SampleCount);
            }
        }

        public int NumberOfSamples
        {
            get { return numberOfSamples; }
            set
            {
                numberOfSamples = value;
                RaisePropertyChanged(() => NumberOfSamples);
            }
        }

        public bool IsNumberSuffixSelected
        {
            get { return isNumberSuffixSelected; }
            set
            {
                isNumberSuffixSelected = value;
                RaisePropertyChanged(() => IsNumberSuffixSelected);
            }
        }

        public bool IsJsonFormatSelected
        {
            get { return isJsonFormatSelected; }
            set
            {
                isJsonFormatSelected = value;
                RaisePropertyChanged(() => IsJsonFormatSelected);
            }
        }

        public bool IsCollecting
        {
            get { return isCollecting; }
            set
            {
                isCollecting = value;
                RaisePropertyChanged(() => IsCollecting);
            }
        }

        public bool IsContinuous
        {
            get { return isContinuous; }
            set
            {
                isContinuous = value;
                RaisePropertyChanged(() => IsContinuous);
            }
        }

        public bool IsXmlFormatSelected
        {
            get { return isXmlFormatSelected; }
            set
            {
                isXmlFormatSelected = value;
                RaisePropertyChanged(() => IsXmlFormatSelected);
            }
        }

        public double ProgressVal
        {
            get { return progressVal; }
            set
            {
                progressVal = value;
                RaisePropertyChanged(() => ProgressVal);
            }
        }

        public double CurrentXPos
        {
            get { return currentXPos; }
            set
            {
                currentXPos = value;
                RaisePropertyChanged(() => CurrentXPos);
            }
        }

        public double CurrentYPos
        {
            get { return currentYPos; }
            set
            {
                currentYPos = value;
                RaisePropertyChanged(() => CurrentYPos);
            }
        }

        public ObservableCollection<TraceEntry> TraceEntries
        {
            get { return traceEntries; }
        }

        public RelayCommand BrowseFolderCommand
        {
            get { return browseFolderCommand; }
        }

        public RelayCommand StartCommand
        {
            get { return startCommand; }
        }

        public RelayCommand StopCommand
        {
            get { return stopCommand; }
        }

        public RelayCommand ExportCommand
        {
            get { return exportCommand; }
        }

        private Session CurrentSession
        {
            get
            {
                if (sessions.Count > 0)
                    return sessions.Last();

                return null;
            }
        }

        private void BrowseFolder()
        {
            OpenDialogView openDialog = new OpenDialogView();
            OpenDialogViewModel openVM = (OpenDialogViewModel)openDialog.DataContext;
            openVM.IsDirectoryChooser = true;
            openVM.Owner = MainWindow.Instance;
            openVM.StartupLocation = System.Windows.WindowStartupLocation.CenterOwner;
            openVM.Caption = "Browse Folder";
            openVM.OpenText = "Select Folder";

            bool? result = openVM.Show();
            if(result.HasValue && result.Value)
                OutputFolder = openVM.SelectedFolder.Path;
        }

        private bool CanStart()
        {
            return !isCollecting;
        }

        private void StartCollecting()
        {
            SetBusyIndicatorText();
            progressIncrement = 100D / NumberOfSamples;
            IsCollecting = true;
            sessions.Add(new Session { SessionNumber = ++sessionCount });

            StopCommand.RaiseCanExecuteChanged();
        }

        private bool CanStop()
        {
            return isCollecting;
        }

        private void StopCollecting()
        {
            SampleCount = 0;
            IsCollecting = false;
            ProgressVal = 0;
            StartCommand.RaiseCanExecuteChanged();
            ExportCommand.RaiseCanExecuteChanged();
        }

        private bool CanExport()
        {
            return sessions.Count > 0 && Directory.Exists(OutputFolder) && !string.IsNullOrWhiteSpace(FileNamePrefix) && IsFileNameValid(fileNamePrefix);
        }

        private bool IsFileNameValid(string fileName)
        {
            List<string> illegalChars = new List<string> { ".", ";", ":", "/", @"\", "<", ">", "*", "'", "\"", "?", "|"};

            foreach(var illChar in illegalChars)
            {
                if (fileName.Contains(illChar))
                    return false;
            }

            return true;
        }

        private void Export()
        {
            foreach (var session in sessions)
            {
                if (IsXmlFormatSelected && !IsJsonFormatSelected)
                    FileExporter.ExportXml(session, OutputFolder, FileNamePrefix, IsNumberSuffixSelected);
                else if (IsJsonFormatSelected && !IsXmlFormatSelected)
                    FileExporter.ExportJson(session, OutputFolder, FileNamePrefix, IsNumberSuffixSelected);
                else
                    FileExporter.ExportCsv(session, OutputFolder, FileNamePrefix, IsNumberSuffixSelected);
            }

            string selectedFormat = IsXmlFormatSelected ? "XML" : IsJsonFormatSelected ? "JSON" : "CSV";

            RadWindow.Alert(new DialogParameters
            {
                Content = string.Format("Successfully exported {0} {1} files to:\n{2}", sessions.Count, selectedFormat, OutputFolder),
                Header = "Files Exported"
            });
        }

        private void SetBusyIndicatorText()
        {
            BusyIndicatorText = IsContinuous ? "Collecting Signal Strength Samples...." : string.Format("Collecting [{0} / {1}] Samples....", SampleCount, NumberOfSamples);
        }

        private void CollectTraces()
        {
            while (true)
            {
                if (isCollecting)
                {
                    int numOfEntries = traceEntries.Count;
                    var traceEntry = wLanAdapter.CollectTraceEntry(++numOfEntries);
                    traceEntry.Position = new Position { X = currentXPos, Y = currentYPos };

                    MainWindow.Instance.Dispatcher.Invoke(() => ProgressVal += progressIncrement);
                    MainWindow.Instance.Dispatcher.Invoke(() => RegisterTraceEntry(traceEntry));

                    if (!IsContinuous)
                    {
                        if (sampleCount == NumberOfSamples - 1)
                        {
                            MainWindow.Instance.Dispatcher.Invoke(StopCollecting);
                        }
                        else
                        {
                            MainWindow.Instance.Dispatcher.Invoke(() => SampleCount++);
                            MainWindow.Instance.Dispatcher.Invoke(() => SetBusyIndicatorText());
                        }
                    }
                }

                Thread.Sleep(TimeSpan.FromSeconds(ScanInterval));
            }
        }

        private void RegisterTraceEntry(TraceEntry item)
        {
            CurrentSession.SessionTraces.Add(item);
            traceEntries.Add(item);
        }

    }
}
